<!DOCTYPE html>
<html>
	<head>
		<meta charset="utf-8">
		<title>深浅拷贝</title>
	</head>
	<body>
		一：浅拷贝
		(1)浅拷贝是创建一个新对象，这个对象有原始对象属性值的一份精确拷贝。如果属性是基本类型，拷贝的就是基本类型。
		如果属性是引用类型，拷贝的就是内存地址，其中一个对象的改变另一个对象也跟着改变

		二：深拷贝
		(1)深拷贝是将一个对象从内存堆中完整的拷贝一份出来，从内存堆中开辟一点新的区域存放新的对象，且修改新对象不会影响原来对象

		三：浅拷贝、深拷贝、赋值的区别
		(1)赋 值：当我们把一个对象赋值给一个新的变量时，赋的其实是该对象的在栈中的地址，而不是堆中的数据。
		也就是两个对象指向的是同一个存储空间，无论哪个对象改变，另一个也会跟着改变。
		(2)浅拷贝：重新在堆中创建内存，拷贝前后的对象的基本数据类型互不影响，但拷贝前后对象的引用类型因共享同一块内存，会相互影响；
		(3)深拷贝：从堆内存中开辟一个新的区域存放新对象，对象中的子对象进行递归拷贝，拷贝前后的两个对象互不影响。

	</body>
	<script type="text/javascript">
		let person = {
			name: "诸葛亮",
			hobby: ['学习', ['睡觉'], '跑步'],
			idCar:undefined,
			tel:null,
			obj: {
				name: '我是一个对象',
				id: 1
			},
			date: new Date(0), 
			reg: new RegExp('/我是一个正则/ig'),
			[Symbol('1')]: 1,
			fun: function() {}
		}

		/*
		一：浅拷贝:
		（1）对象实现方法：Object.assign(target, source)、Object.create()、手动for(var i in obj){}实现、...扩展运算符
		（2）数组实现方法：arr.slice(begin, end);arr.concat(begin, end);
		*/
		// 对象浅拷贝案例：
		function shallowCopy(obj) {
			var target = {};
			for (var i in obj) {
				if (obj.hasOwnProperty()) {
					target[i] = obj[i]
				}
			}
		}
		var person1 = shallowCopy(person);
		var person1 = Object.create(person);
		var person1 = Object.assign({}, person)
		var person1 = {
			...person
		}
		// person1.name = '刘备';
		// person1.hobby[0] = '唱歌'
		// console.log(person, person1)
		// 结果是原对象的name没有改变，但是原对象的hobby改变了

		// 数组浅拷贝案例：

		var arr1 = [1, 2, 3, {
			m: 18
		}]
		var arr2 = arr1.slice()
		var arr2 = arr1.concat()
		arr2[0] = 'mm'
		arr2[3].m = 'pp'
		console.log(arr1)
		console.log(arr2)
		// 结果是原数组的第0项没有改变，但是原数组的第3项对象值改变了




		/*
		二：深拷贝:
		方法一：乞丐版JSON.parse(JSON.stringfy()) 、递归实现
		*/

		const isComplexDataType = obj => (typeof obj === 'object' || typeof obj === 'function') && (obj !== null)
		const deepClone = function(obj, hash = new WeakMap()) {
			if (obj.constructor === Date) return new Date(obj) // 日期对象直接返回一个新的日期对象
			if (obj.constructor === RegExp) return new RegExp(obj) //正则对象直接返回一个新的正则对象
			//如果循环引用了就用 weakMap 来解决
			if (hash.has(obj)) return hash.get(obj)
			let allDesc = Object.getOwnPropertyDescriptors(obj)
			//遍历传入参数所有键的特性
			let cloneObj = Object.create(Object.getPrototypeOf(obj), allDesc)
			//继承原型链
			hash.set(obj, cloneObj)
			for (let key of Reflect.ownKeys(obj)) {
				cloneObj[key] = (isComplexDataType(obj[key]) && typeof obj[key] !== 'function') ? deepClone(obj[key],
					hash) : obj[key]
			}
			return cloneObj
		}
		// var person1 = JSON.parse(JSON.stringfy(person));//上面的reg成了空对象 ， fun丢失，idCar丢失
		
		var person1 = deepClone(person);
		person1.name = '刘备';
		person1.hobby[0] = '唱歌'
		console.log(person, person1)
	</script>
</html>
